package com.jhte.scheduleJob.service;

import com.jhte.scheduleJob.base.util.BaseMsg;
import com.jhte.scheduleJob.base.util.BaseResponse;
import com.jhte.scheduleJob.base.util.ConcurrentDateUtil;
import com.jhte.scheduleJob.dao.ScheduleJobDao;
import com.jhte.scheduleJob.entity.QuartzLog;
import com.jhte.scheduleJob.entity.ScheduleJob;
import com.jhte.scheduleJob.job.ConcurrentQuartzJobFactory;
import com.jhte.scheduleJob.job.MonitorJobListener;
import com.jhte.scheduleJob.job.QuartzJobFactory;
import com.jhte.scheduleJob.util.ScheduleConstant;
import com.jhte.scheduleJob.util.SpringUtils;
import com.jhte.scheduleJob.vo.JobKeyRequest;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * @author wuchun
 * @description
 * @create 2018-01-31 11:59
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class ScheduleJobServiceImpl implements ScheduleJobService {

    @Autowired
    private ScheduleJobDao scheduleJobDao;

    //加入Qulifier注解，通过名称注入bean
    @Autowired
    @Qualifier("Scheduler")
    private Scheduler scheduler;

    /**
     * 添加任务
     *
     * @param job
     * @throws SchedulerException
     */
    @Override
    public void addScheduleJob(ScheduleJob job) throws SchedulerException, ParseException {
        if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) {
            return;
        }

        TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        Date triggerStartTime = ConcurrentDateUtil.parseStrDate(job.getStartTime());
        //定时任务触发器不存在，创建一个
        if (null == trigger) {
            Class clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class
                    : ConcurrentQuartzJobFactory.class;
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
            String jobJson = JSONObject.fromObject(job).toString();
            jobDetail.getJobDataMap().put("scheduleJob", jobJson);
            //表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
            //按给定的cronExpression表达式构建trigger
            if (StringUtils.isNotBlank(job.getEndTime())) {
                Date triggerEndTime = ConcurrentDateUtil.parseStrDate(job.getEndTime());
                trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
                        .startAt(triggerStartTime).endAt(triggerEndTime)
                        .withSchedule(scheduleBuilder).build();
            } else {
                trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
                        .startAt(triggerStartTime).withSchedule(scheduleBuilder).build();
            }
            //执行job
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            // Trigger已存在，那么更新相应的定时设置
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
            // 按新的cronExpression表达式重新构建trigger
            if (StringUtils.isNotBlank(job.getEndTime())) {
                Date triggerEndTime = ConcurrentDateUtil.parseStrDate(job.getEndTime());
                trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
                        .startAt(triggerStartTime).endAt(triggerEndTime)
                        .withSchedule(scheduleBuilder).build();
            } else {
                trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
                        .startAt(triggerStartTime).withSchedule(scheduleBuilder).build();
            }
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
        /*JobListener jobListener = new MonitorJobListener();
        scheduler.getListenerManager().addJobListener(jobListener);*/
        // 启动调度器
        scheduler.start();
    }

    /**
     * 更新任务
     *
     * @param job
     * @throws SchedulerException
     */
    @Override
    public void updateScheduleJob(ScheduleJob job) throws SchedulerException, ParseException {
        TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
        // 表达式调度构建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        Date triggerStartTime = ConcurrentDateUtil.parseStrDate(job.getStartTime());
        // 按新的cronExpression表达式重新构建trigger
        if (StringUtils.isNotBlank(job.getEndTime())) {
            Date triggerEndTime = ConcurrentDateUtil.parseStrDate(job.getEndTime());
            trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
                    .startAt(triggerStartTime).endAt(triggerEndTime)
                    .withSchedule(scheduleBuilder).build();
        } else {
            trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
                    .startAt(triggerStartTime).withSchedule(scheduleBuilder).build();
        }
        // 按新的trigger重新设置job执行
        scheduler.rescheduleJob(triggerKey, trigger);
    }

    /**
     * 暂停任务
     *
     * @param jobKeyRequest
     * @throws SchedulerException
     */
    @Override
    public void pauseScheduleJob(JobKeyRequest jobKeyRequest) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(jobKeyRequest.getJobName(), jobKeyRequest.getJobGroup()));
    }

    /**
     * 恢复任务
     *
     * @param jobKeyRequest
     * @throws SchedulerException
     */
    @Override
    public void resumeScheduleJob(JobKeyRequest jobKeyRequest) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(jobKeyRequest.getJobName(), jobKeyRequest.getJobGroup()));
    }

    /**
     * 删除任务
     *
     * @param jobKeyRequest
     * @throws SchedulerException
     */
    @Override
    public void deleteScheduleJob(JobKeyRequest jobKeyRequest) throws SchedulerException {
        //scheduler.pauseTrigger(TriggerKey.triggerKey(jobKeyRequest.getJobName(), jobKeyRequest.getJobGroup()));
        //scheduler.unscheduleJob(TriggerKey.triggerKey(jobKeyRequest.getJobName(), jobKeyRequest.getJobGroup()));
        scheduler.deleteJob(JobKey.jobKey(jobKeyRequest.getJobName(), jobKeyRequest.getJobGroup()));
    }

    /**
     * 立即执行job
     *
     * @param jobKeyRequest
     * @throws SchedulerException
     */
    @Override
    public void executeJobNow(JobKeyRequest jobKeyRequest) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobKeyRequest.getJobName(), jobKeyRequest.getJobGroup());
        //JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        //jobDetail.getJobDataMap().put("execType", ScheduleConstant.EXECTYPE_UNPLANNED);
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("execType", ScheduleConstant.EXECTYPE_UNPLANNED);
        scheduler.triggerJob(jobKey, jobDataMap);
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     * @throws SchedulerException
     */
    @Override
    public List<ScheduleJob> getAllJobs() throws Exception {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                //赋值
                ScheduleJob job = transScheduleJob(jobKey, trigger);
                jobList.add(job);
            }
        }
        return jobList;
    }

    /**
     * 所有正在运行的job
     *
     * @return
     * @throws SchedulerException
     */
    @Override
    public List<ScheduleJob> getRunningJobs() throws Exception {
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            //赋值
            ScheduleJob job = transScheduleJob(jobKey, trigger);
            jobList.add(job);
        }
        return jobList;
    }

    private ScheduleJob transScheduleJob(JobKey jobKey, Trigger trigger) throws Exception {
        ScheduleJob job = new ScheduleJob();
        job.setJobName(jobKey.getName());
        job.setJobGroup(jobKey.getGroup());
        job.setDescription("触发器:" + trigger.getKey());
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        job.setJobStatus(triggerState.name());
        if (trigger instanceof CronTrigger) {
            CronTrigger cronTrigger = (CronTrigger) trigger;
            job.setCronExpression(cronTrigger.getCronExpression());
            job.setStartTime(ConcurrentDateUtil.formatDate(cronTrigger.getStartTime()));
            job.setEndTime(ConcurrentDateUtil.formatDate(cronTrigger.getEndTime()));
        }
        return job;
    }

    /**
     * 检查执行任务的类和方法是否存在
     *
     * @param scheduleJob
     * @return
     * @throws Exception
     */
    @Override
    public BaseResponse checkClassAndMethod(ScheduleJob scheduleJob) throws Exception {
        BaseResponse baseResponse = new BaseResponse();
        baseResponse.setCode(BaseMsg.CODE_FAILED);
        try {
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
        } catch (Exception e) {
            baseResponse.setMsg("cron表达式有误，不能被解析！");
            return baseResponse;
        }
        Object obj = null;
        try {
            if (StringUtils.isNotBlank(scheduleJob.getSpringId())) {
                obj = SpringUtils.getBean(scheduleJob.getSpringId());
            } else {
                Class clazz = Class.forName(scheduleJob.getBeanClass());
                obj = clazz.newInstance();
            }
        } catch (Exception e) {
            baseResponse.setMsg("目标类反射有误！");
            return baseResponse;
        }
        if (obj == null) {
            baseResponse.setMsg("未找到目标类！");
            return baseResponse;
        } else {
            Class clazz = obj.getClass();
            Method method = null;
            try {
                //无参数则不设置参数
                if (StringUtils.isBlank(scheduleJob.getParameter())) {
                    method = clazz.getDeclaredMethod(scheduleJob.getMethodName(), new Class<?>[]{});
                    //method = clazz.getMethod(scheduleJob.getMethodName(), null);
                } else {
                    method = clazz.getDeclaredMethod(scheduleJob.getMethodName(), Object[].class);
                }
            } catch (Exception e) {
                baseResponse.setMsg("目标方法反射有误！");
                return baseResponse;
            }
            if (method == null) {
                baseResponse.setMsg("未找到目标方法！");
                return baseResponse;
            }
        }
        baseResponse.success();
        return baseResponse;
    }

}
